package org.freeocs.action;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.converters.SqlDateConverter;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.freeocs.commons.exception.ActionException;
import org.freeocs.commons.page.PropertyFilter;
import org.freeocs.commons.page.PropertyFilter.MatchType;
import org.freeocs.commons.util.CryptUtils;
import org.freeocs.commons.util.RequestUtils;
import org.freeocs.commons.util.ResourceUtils;
import org.freeocs.entity.User;

/**
 * Action类常用工具
 * 
 * @author chirs
 */
public class ActionUtils {
	static {
		ConvertUtils.register(new SqlDateConverter(null), java.sql.Date.class);
		ConvertUtils.register(new Converter() {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d");
			SimpleDateFormat sdf_time = new SimpleDateFormat("yyyy-M-d H:m");

			@SuppressWarnings("unchecked")
			public Object convert(Class type, Object value) {
				if (value == null)
					return null;
				if (value instanceof Date)
					return (value);
				try {
					return sdf_time.parse(value.toString());
				} catch (ParseException e) {
					try {
						return sdf.parse(value.toString());
					} catch (ParseException e1) {
						return null;
					}
				}
			}
		}, java.util.Date.class);
	}

	/**
	 * 将HTTP请求参数映射到bean对象中
	 * 
	 * @param req
	 * @param beanClass
	 * @return
	 * @throws Exception
	 */
	public static <T> T _ParamsToBean(HttpServletRequest req, Class<T> beanClass) {
		try {
			T bean = beanClass.newInstance();
			BeanUtils.populate(bean, req.getParameterMap());
			return bean;
		} catch (Exception e) {
			e.printStackTrace();
			throw new ActionException(e.getMessage());
		}
	}

	public static ActionException _FromResource(HttpServletRequest req, String bundle, String key, Object... args) {
		String res = ResourceUtils.getStringForLocale(req.getLocale(), bundle, key, args);
		return new ActionException(res);
	}

	public static ActionException _FromErrorResource(HttpServletRequest req, String key, Object... args) {
		return _FromResource(req, "error", key, args);
	}

	/**
	 * 输出信息到浏览器
	 * 
	 * @param res
	 * @param msg
	 * @throws IOException
	 */
	public static void _OutputToResponse(HttpServletResponse res, Object msg) throws IOException {
		if (!UTF8.equalsIgnoreCase(res.getCharacterEncoding()))
			res.setCharacterEncoding(UTF8);
		res.getWriter().print(msg);
	}

	public static String _MakeJSON(String key, Object value) {
		boolean isNum = value instanceof Number;
		StringBuilder json = new StringBuilder("{\"");
		json.append(key);
		json.append("\":");
		if (!isNum)
			json.append("\"");
		json.append(value);
		if (!isNum)
			json.append("\"");
		json.append("}");
		return json.toString();
	}

	public static String _MakeJSON(String[] key, Object[] value) {
		StringBuilder json = new StringBuilder("{");
		for (int i = 0; i < key.length; i++) {
			if (i > 0)
				json.append(',');
			boolean isNum = value[i] instanceof Number;
			json.append("\"");
			json.append(key[i]);
			json.append("\":");
			if (!isNum)
				json.append("\"");
			json.append(value[i]);
			if (!isNum)
				json.append("\"");
		}
		json.append("}");
		return json.toString();
	}

	/**
	 * 保存登录信息
	 * 
	 * @param req
	 * @param res
	 * @param user
	 * @param save
	 */
	public static void _SaveLogin(HttpServletRequest req, HttpServletResponse res, User user, boolean save) {
		String ip = RequestUtils.getRemoteAddr(req);
		String new_value = _GenLoginKey(user, ip, req.getHeader("user-agent"));
		int max_age = save ? MAX_AGE : -1;
		RequestUtils.deleteCookie(req, res, COOKIE_LOGIN, true);
		RequestUtils.setCookie(req, res, COOKIE_LOGIN, new_value, max_age, true);
	}

	public static void _DeleteLogin(HttpServletRequest req, HttpServletResponse res) {
		RequestUtils.deleteCookie(req, res, COOKIE_LOGIN, true);
	}

	/**
	 * 生成用户登录标识字符串
	 * 
	 * @param user
	 * @param ip
	 * @param user_agent
	 * @return
	 */
	public static String _GenLoginKey(User user, String ip, String user_agent) {
		StringBuilder sb = new StringBuilder();
		sb.append(user.getId());
		sb.append('|');
		sb.append(user.getPwd());
		sb.append('|');
		sb.append(ip);
		sb.append('|');
		sb.append((user_agent == null) ? 0 : user_agent.hashCode());
		sb.append('|');
		sb.append(System.currentTimeMillis());
		return _Encrypt(sb.toString());
	}

	/**
	 * 从cookie中读取保存的用户信息
	 * 
	 * @param req
	 * @return
	 */
	public static User _UserFromCookie(HttpServletRequest req) {
		try {
			Cookie cookie = RequestUtils.getCookie(req, COOKIE_LOGIN);
			if (cookie != null && StringUtils.isNotBlank(cookie.getValue())) {
				String ck = _Decrypt(cookie.getValue());
				String[] items = StringUtils.split(ck, '|');
				if (items.length == 5) {
					String ua = req.getHeader("user-agent");
					int ua_code = (ua == null) ? 0 : ua.hashCode();
					int old_ua_code = Integer.parseInt(items[3]);
					if (ua_code == old_ua_code) {
						User user = new User();
						user.setId(NumberUtils.toInt(items[0], -1));
						user.setPwd(items[1]);
						return user;
					}
				}
			}
		} catch (Exception e) {
		}
		return null;
	}

	/**
	 * 根据按PropertyFilter命名规则的Request参数,创建PropertyFilter列表.
	 * PropertyFilter命名规则为Filter属性前缀_比较类型_属性名.
	 * 
	 * eg. filter_EQUAL_name filter_LIKE_name|email
	 */
	public static List<PropertyFilter> buildPropertyFilters(final HttpServletRequest request, final String filterPrefix) {
		List<PropertyFilter> filterList = new ArrayList<PropertyFilter>();
		// 分析参数Map,构造PropertyFilter列表
		for (Map.Entry<String, String> entry : getParametersStartingWith(request, filterPrefix).entrySet()) {
			String filterName = entry.getKey();
			String value = entry.getValue();
			// 如果value值为空,则忽略此filter.
			if (!StringUtils.isBlank(value)) {
				// 分析filterName,获取matchType与propertyName
				String[] parts = StringUtils.split(filterName, "_");
				MatchType matchType;
				try {
					matchType = Enum.valueOf(MatchType.class, parts[1]);
				} catch (RuntimeException e) {
					throw new IllegalArgumentException("filter名称没有按规则编写,无法得到属性比较类型.", e);
				}
				PropertyFilter filter = new PropertyFilter(parts[0], parts[2], value, matchType);
				filterList.add(filter);
			}
		}
		return filterList;
	}

	/**
	 * 从request中获取含属性前缀名的参数,构造去除前缀名后的参数Map.
	 * 
	 * @param request
	 * @param prefix
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, String> getParametersStartingWith(HttpServletRequest request, String prefix) {
		Enumeration paramNames = request.getParameterNames();
		Map<String, String> params = new TreeMap<String, String>();
		if (prefix == null) {
			prefix = "";
		}
		while (paramNames != null && paramNames.hasMoreElements()) {
			String paramName = (String) paramNames.nextElement();
			if ("".equals(prefix) || paramName.startsWith(prefix)) {
				String values = request.getParameter(paramName);
				if (values != null) {
					params.put(paramName.substring(prefix.length()), values);
				}
			}
		}
		return params;
	}

	public static String buildFilterCriterions(final List<PropertyFilter> filters) {
		StringBuilder sb = new StringBuilder();
		for (PropertyFilter filter : filters) {
			String propertyName = filter.getPropertyName();
			if (!StringUtils.contains(propertyName, PropertyFilter.OR_SEPARATOR)) { // properNameName中只有一个属性的情况.
				sb.append(buildPropertyCriterion(propertyName, filter.getValue(), filter.getMatchType(), filter.getPropertyType()));
			} else {// properName中包含多个属性的情况,进行or处理.
				String[] params = StringUtils.split(propertyName, PropertyFilter.OR_SEPARATOR);
				for (String param : params) {
					sb.append(buildPropertyCriterion(param, filter.getValue(), filter.getMatchType(), filter.getPropertyType()));
				}
			}
		}
		return sb.toString();
	}

	/**
	 * 按属性条件参数创建Criterion,辅助函数.
	 */
	protected static String buildPropertyCriterion(final String propertyName, final Object value, final MatchType matchType, final String propertyType) {
		StringBuilder sb = new StringBuilder("and ");
		if (StringUtils.isNotBlank(propertyName)) {
			sb.append(propertyName);
			if ("S".equals(propertyType)) {
				sb.append("'").append(restrictions(matchType)).append("'");
			} else if ("N".equals(propertyType)) {
				sb.append(restrictions(matchType));
			} else if ("D".equals(propertyType)) {
				sb.append(restrictions(matchType));
			}
		}
		return sb.toString();
	}

	protected static String restrictions(final MatchType matchType) {
		if (MatchType.LIKE.equals(matchType)) {
			return "like";
		} else if (MatchType.NE.equals(matchType)) {
			return "<>";
		} else if (MatchType.GT.equals(matchType)) {
			return ">";
		} else if (MatchType.LT.equals(matchType)) {
			return "<";
		} else if (MatchType.GE.equals(matchType)) {
			return ">=";
		} else if (MatchType.LE.equals(matchType)) {
			return "<=";
		} else {
			return "=";
		}
	}

	private final static byte[] E_KEY = new byte[] { '.', 'F', 'R', 'E', 'E', 'O', 'C', 'S' };
	private final static String UTF8 = "utf-8";
	public final static String COOKIE_LOGIN = "freeocsid";
	private final static int MAX_AGE = 86400 * 365;

	/**
	 * 加密
	 * 
	 * @param value
	 * @return
	 * @throws Exception
	 */
	private static String _Encrypt(String value) {
		byte[] data = CryptUtils.encrypt(value.getBytes(), E_KEY);
		try {
			return URLEncoder.encode(new String(Base64.encodeBase64(data)), UTF8);
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	/**
	 * 解密
	 * 
	 * @param value
	 * @return
	 * @throws Exception
	 */
	private static String _Decrypt(String value) {
		try {
			value = URLDecoder.decode(value, UTF8);
			if (StringUtils.isBlank(value))
				return null;
			byte[] data = Base64.decodeBase64(value.getBytes());
			return new String(CryptUtils.decrypt(data, E_KEY));
		} catch (UnsupportedEncodingException excp) {
			return null;
		}
	}
}
